/**************************************************
 * Project Name         [JavaEE_XML]
 * File Name            [JaxpParseXML.java]
 * Creation Date        [08-13-2014]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 **************************************************/
package pkg_01_jaxp;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import pojo.Student;

/**
 * 使用JAXP(Java API for XML Processing)解析XML, 已被JDK7废弃
 * 
 * DOM的基本对象有5个:Document, Node, NodeList, Element和Attr<br/>
 * 
 * Document对象代表了整个XML的文档, 所有其它的Node, 都以一定的顺序包含在Document对象之内, 排列成一个树形的结构<br/>
 * 可以通过遍历这棵树来得到XML文档的所有的内容, 这也是对XML文档操作的起点<br/>
 * 我们总是先通过解析XML源文件而得到一个Document对象, 然后再来执行后续的操作<br/>
 * 
 * Node对象是DOM结构中最为基本的对象, 代表了文档树中的一个抽象的节点<br/>
 * 在实际使用的时候, 很少会真正的用到node这个对象, 而是用到诸如Element, Attr, Text等Node对象的子对象来操作文档<br/>
 * Node对象为这些对象提供了一个抽象的, 公共的根<br/>
 * 虽然在Node对象中定义了对其子节点进行存取的方法, 但是有一些Node子对象, 比如Text对象, 它并不存在子节点<br/>
 * 
 * NodeList对象, 代表了一个包含了一个或者多个Node的列表<br/>
 * 
 * Element对象代表的是XML文档中的标签元素, 继承于Node, 亦是Node的最主要的子对象<br/>
 * 在标签中可以包含有属性, 因而Element对象中有存取其属性的方法<br/>
 * 而任何Node中定义的方法, 也可以用在Element对象上面<br/>
 * 
 * Attr对象代表了某个标签中的属性<br/>
 * Attr继承于Node, 虽然Attr实际上是包含在Element中的, 但它并不能被看作是Element的子对象,
 * 因而在DOM中Attr并不是DOM树的一部分<br/>
 * 所以调用Node中的方法getParentNode(), getPreviousSibling()和getNextSibling()返回的都将是null<br/>
 * 也就是说, Attr其实是被看作包含它的element对象的一部分, 它并不作为DOM树中单独的一个节点出现,
 * 这一点在使用的时候要同其它的node子对象相区别<br/>
 * 
 * 
 * @author 不落的太阳(Sean Yang)
 * @version 1.0
 * @since JDK 6
 * 
 */
public class JaxpParseXML {

	/**
	 * 根据XML创建DOM对象
	 * 
	 * @param fileName
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	private static Document getDocument(String fileName)
			throws ParserConfigurationException, SAXException, IOException {
		// 构建DOM工厂
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		// 从DOM工厂生成DOM builder
		DocumentBuilder builder = factory.newDocumentBuilder();
		// 从XML文档解析DOM
		Document document = builder.parse(new File(fileName));
		// normalize方法可以去掉去掉XML文档中作为格式化内容的空白,避免这些空白映射在DOM树中成为不必要的text node对象
		// 在输出的时候此方法更为有用
		document.normalize();
		return document;
	}

	/**
	 * 使用Element解析XML文件
	 * 
	 * @param fileName
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws DOMException
	 * @throws ParseException
	 */
	public static List<Student> parseXMLFileByElement(String fileName)
			throws ParserConfigurationException, SAXException, IOException,
			DOMException, ParseException {
		List<Student> students = new ArrayList<Student>();

		Document document = getDocument(fileName);

		Student student = null;
		// 获得根节点(Element是Node的子类)
		Element root = document.getDocumentElement();
		NodeList nodeList = root.getElementsByTagName("STUDENT");
		for (int i = 0; i < nodeList.getLength(); i++) {
			Element studentElement = (Element) nodeList.item(i);
			student = new Student();

			// 获得属性
			String studentID = studentElement.getAttribute("STUDENT_ID");
			System.out.println(studentID);

			// Name
			Element nameElement = (Element) studentElement
					.getElementsByTagName("NAME").item(0);
			Text nameText = (Text) nameElement.getFirstChild();
			student.setName(nameText.getNodeValue());
			// student.setName(nameElement.getTextContent());

			// Age
			Element ageElement = (Element) studentElement.getElementsByTagName(
					"AGE").item(0);
			Text ageText = (Text) ageElement.getFirstChild();
			student.setAge(Integer.parseInt(ageText.getNodeValue()));

			// Gender
			Element genderElement = (Element) studentElement
					.getElementsByTagName("GENDER").item(0);
			Text genderText = (Text) genderElement.getFirstChild();
			student.setGender(genderText.getNodeValue());

			// Birthday
			Element birthdayElement = (Element) studentElement
					.getElementsByTagName("BIRTHDAY").item(0);
			Text birthdayText = (Text) birthdayElement.getFirstChild();
			student.setBirthday(new SimpleDateFormat("yyyy-MM-dd")
					.parse(birthdayText.getNodeValue()));

			// Phone
			Element phoneElement = (Element) studentElement
					.getElementsByTagName("PHONENUMBER").item(0);
			Text phoneText = (Text) phoneElement.getFirstChild();
			student.setPhoneNumber(phoneText.getNodeValue());

			students.add(student);
		}

		return students;
	}

	/**
	 * 使用Node解析XML文件
	 * 
	 * @param fileName
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws DOMException
	 * @throws ParseException
	 */
	public static List<Student> parseXMLFileByNode(String fileName)
			throws ParserConfigurationException, SAXException, IOException,
			DOMException, ParseException {
		List<Student> students = new ArrayList<Student>();

		Document document = getDocument(fileName);

		Student student = null;

		NodeList studentNodes = document.getElementsByTagName("STUDENT");
		for (int i = 0; i < studentNodes.getLength(); i++) {
			student = new Student();
			Node studentNode = studentNodes.item(i);

			NodeList nodes = studentNode.getChildNodes();
			for (int j = 0; j < nodes.getLength(); j++) {
				Node infoNode = nodes.item(j);
				if (infoNode instanceof Element) {// 去掉不同元素之间的空白#text元素
					if ("name".equalsIgnoreCase(infoNode.getNodeName())) {
						student.setName(infoNode.getTextContent());
					}
					if ("age".equalsIgnoreCase(infoNode.getNodeName())) {
						student.setAge(Integer.parseInt(infoNode
								.getTextContent()));
					}
					if ("gender".equalsIgnoreCase(infoNode.getNodeName())) {
						student.setGender(infoNode.getTextContent());
					}
					if ("birthday".equalsIgnoreCase(infoNode.getNodeName())) {
						student.setBirthday(new SimpleDateFormat("yyyy-MM-dd")
								.parse(infoNode.getTextContent()));
					}
					if ("phonenumber".equalsIgnoreCase(infoNode.getNodeName())) {
						student.setPhoneNumber(infoNode.getTextContent());
					}
				}
			}
			students.add(student);
		}
		return students;
	}

	/**
	 * 创建新的XML文件
	 * 
	 * @param fileName
	 * @param students
	 * @throws ParserConfigurationException
	 * @throws TransformerException
	 */
	public static void createXMLFileByElement(String fileName,
			List<Student> students) throws ParserConfigurationException,
			TransformerException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document document = builder.newDocument();

		// 创建根元素并添加到XML文档中
		Element root = document.createElement("学生名册");
		document.appendChild(root);

		// 添加学生元素
		for (Student student : students) {
			Element studentElement = document.createElement("学生");
			root.appendChild(studentElement);

			// 设置属性
			studentElement.setAttribute("STUDENT_ID", "s00003");

			Element nameElement = document.createElement("学生姓名");
			studentElement.appendChild(nameElement);
			// Text nameText = document.createTextNode(student.getName());
			// nameElement.appendChild(nameText);
			nameElement.setTextContent(student.getName());

			Element ageElement = document.createElement("学生年龄");
			studentElement.appendChild(ageElement);
			Text ageText = document.createTextNode(String.valueOf(student
					.getAge()));
			ageElement.appendChild(ageText);

			Element genderElement = document.createElement("学生性别");
			studentElement.appendChild(genderElement);
			Text genderText = document.createTextNode(student.getGender());
			genderElement.appendChild(genderText);

			Element birthdayElement = document.createElement("出生日期");
			studentElement.appendChild(birthdayElement);
			Text birthdayText = document.createTextNode(new SimpleDateFormat(
					"yyyy-MM-dd").format(student.getBirthday()));
			birthdayElement.appendChild(birthdayText);

			Element phoneElement = document.createElement("电话号码");
			studentElement.appendChild(phoneElement);
			Text phoneText = document.createTextNode(student.getPhoneNumber());
			phoneElement.appendChild(phoneText);
		}

		transformDOMToXML(fileName, document);
	}

	/**
	 * 将DOM对象写出到XML
	 * 
	 * @param fileName
	 * @param document
	 * @throws TransformerFactoryConfigurationError
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 */
	private static void transformDOMToXML(String fileName, Document document)
			throws TransformerFactoryConfigurationError,
			TransformerConfigurationException, TransformerException {
		// 创建一个TransformerFactory
		TransformerFactory tFactory = TransformerFactory.newInstance();
		// 创建一个Transformer对象
		// Transformer类相当于一个XSLT引擎, 通常我们使用它来处理XSL文件,但是在这里用来输出XML文档
		Transformer transformer = tFactory.newTransformer();
		Properties props = transformer.getOutputProperties();
		// 设置新的输出属性: 输出字符编码为"UTF-8", 这样可以支持中文字符
		props.setProperty(OutputKeys.ENCODING, "UTF-8");
		// 设置新的输出属性: 设置输出为XML格式
		props.setProperty(OutputKeys.METHOD, "xml");
		transformer.setOutputProperties(props);

		// 创建一个DOMSource对象,该构造函数的参数可以是一个Document对象
		DOMSource source = new DOMSource(document);
		// 创建XSLT引擎的输出对象, 这里将输出写入文件
		File file = new File(fileName);
		StreamResult result = new StreamResult(file);

		// 执行DOM文档到XML文件的转换
		transformer.transform(source, result);
	}

	public static void main(String[] args) {
		try {
			List<Student> students = parseXMLFileByElement("src/xml/Student.xml");
			System.out.println(students);

			System.out.println();
			students = parseXMLFileByNode("src/xml/Student.xml");
			System.out.println(students);

			System.out.println();
			// createXMLFileByElement("src/xml/Student2.xml", students);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
